package JAVA并发;


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


/**
 * @author zhangmin
 * @create 2022-03-15 17:00
 *
 * 四、互斥同步
 * Java 提供了两种锁机制来控制多个线程对共享资源的互斥访问，第一个是 JVM 实现的 synchronized，而另一个是 JDK 实现的 ReentrantLock
 * synchronized：
 *               同步一个代码块：只作用于同一个对象，两个以上对象上的同步代码块不会同步
 *               同步一个方法：它和同步代码块一样，作用于同一个对象。
 *               同步一个类：作用于整个类，也就是说两个线程调用同一个类的不同对象上的这种同步语句，也会进行同步。
 *               同步一个静态方法：作用于整个类
 * ReentrantLock： java.util.concurrent（J.U.C）包中的锁。
 *
 * 比较：
 *      1、synchronized 是 JVM 实现的，而 ReentrantLock 是 JDK 实现的。
 *      2、新版本 Java 对 synchronized 进行了很多优化，例如自旋锁等，synchronized 与 ReentrantLock 大致相同。
 *      3、当持有锁的线程长期不释放锁的时候，正在等待的线程可以选择放弃等待，改为处理其他事情，也就是等待可中断。ReentrantLock 可中断，而 synchronized 不行。
 *      4、公平锁是指多个线程在等待同一个锁时，必须按照申请锁的时间顺序来依次获得锁。synchronized 中的锁是非公平的，ReentrantLock 默认情况下也是非公平的，但是也可以是公平的。
 *      5、一个 ReentrantLock 可以同时绑定多个 Condition 对象
 * 除非需要使用 ReentrantLock 的高级功能，否则优先使用 synchronized。这是因为 synchronized 是 JVM 实现的一种锁机制，JVM 原生地支持它，
 * 而 ReentrantLock 不是所有的 JDK 版本都支持。并且使用 synchronized 不用担心没有释放锁而导致死锁问题，因为 JVM 会确保锁的释放。
 */
public class synchronized_ReentrantLock4 {
    public void fun1()  {
        synchronized (this){
            //同步代码块，多线程打印0-9
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(i+ "");
            }
        }
    }

    public synchronized void fun2(){
        //同步一个方法
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(100);
                System.out.println(i+" ");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

    public void fun3(){
        synchronized (synchronized_ReentrantLock4.class){
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(i+" ");
            }
        }
    }

    public synchronized static void fun4(){
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(i+ " ");
        }
    }

    //ReentrantLock锁
    private Lock lock=new ReentrantLock();
    public void fun5(){
        //加锁
        lock.lock();
        try {
            for (int i = 0; i < 10; i++) {
                Thread.sleep(100);
                System.out.println(i+" ");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //解锁，避免发生死锁
            lock.unlock();
        }

    }

    public static void main(String[] args) {
        synchronized_ReentrantLock4 e1=new synchronized_ReentrantLock4();
        synchronized_ReentrantLock4 e2=new synchronized_ReentrantLock4();
        ExecutorService executorService=Executors.newCachedThreadPool();
        //两个线程调用同一个对象的方法--会同步打印
        /*executorService.execute(()->e1.fun1());
        executorService.execute(()->e1.fun1());*/
        //两个线程调用不同的对象，不会同步，交替打印
        /*executorService.execute(()->e1.fun1());
        executorService.execute(()->e2.fun1());*/

        //同步方法--用同一个对象调用会同步，用不同对象调用不会同步交替打印
        /*executorService.execute(()->e1.fun2());
        executorService.execute(()->e1.fun2());
        executorService.execute(()->e2.fun2());*/

        //同步类：用不同的对象调用也会同步
        /*executorService.execute(()->e1.fun3());
        executorService.execute(()->e2.fun3());*/

        //同步静态方法:静态对象属于类不属于对象，直接用类名调用，会同步
        /*executorService.execute(()->synchronized_ReentrantLock4.fun4());
        executorService.execute(()->synchronized_ReentrantLock4.fun4());*/


        //ReentrantLock，对同一个对象同步，对不同对象需要看加锁的位置
        executorService.execute(()->e1.fun5());
        executorService.execute(()->e1.fun5());
    }

}
